package geo.math;

import java.util.ArrayList;
import geo.obj.PhiObject;
import geo.obj.Triangle;

public class GMath {
	
	/**
	 * This method calculates the length between two points
	 * @param x1 x coordinate of first point
	 * @param y1 y coordinate of first point
	 * @param x2 x coordinate of second point
	 * @param y2 y coordinate of second point
	 * @return length between two points
	 */
	public static double length(double x1, double y1, double x2, double y2){
		return Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow(y1 - y2, 2));
	}
	
	/**
	 * This method checks point on line
	 * @param x x coordinate of checking point
	 * @param y y coordinate of checking point
	 * @param x1 x coordinate of first point of line
	 * @param y1 y coordinate of first point of line
	 * @param x2 x coordinate of second point of line
	 * @param y2 y coordinate of second point of line
	 * @return returns true if line contains checking point
	 */
	public static boolean isOnLine(double x, double y, double x1, double y1, double x2, double y2){
		double l1 = length(x1, y1, x, y);
		double l2 = length(x, y, x2, y2);
		double l = length(x1, y1, x2, y2);
		if (l == (l1 + l2)) return true;
		else return false;
	}
	
	/**
	 * This method returns true if lines are intersected
	 * @param x1 - x coordinate of first point
	 * @param y1 - y coordinate of first point
	 * @param x2 - x coordinate of second point
	 * @param y2 - y coordinate of second point
	 * @param x3 - x coordinate of third point
	 * @param y3 - y coordinate of third point
	 * @param x4 - x coordinate of fourth point
	 * @param y4 - y coordinate of fourth point
	 * @return true or false
	 */
	public static boolean isIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4){
		double d = (x1 - x2) * (y4 - y3) - (y1 - y2) * (x4 - x3);
		double da = (x1 - x3) * (y4 - y3) - (y1 - y3) * (x4 - x3);
		double db = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3);
		double ta;
		double tb;
		
		if (Math.abs(d) == 0){
			return false;
		}
		else{
			ta = da/d;
			tb = db/d;
			if((0 <= ta) && (ta <=1) && (0 <= tb) && (tb <= 1)){		
				return true;
				}
				else
					return false;
			}		
	}
	
	/**
	 * This method finds point of line intersect
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @param x3
	 * @param y3
	 * @param x4
	 * @param y4
	 * @return double[0] - x coordinate, double[1] - y coordinate
	 */
	public static double[] lineIntersectPoint(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4){
		double[] xy = new double[2];
		
		double d = (x1 - x2) * (y4 - y3) - (y1 - y2) * (x4 - x3);
		double da = (x1 - x3) * (y4 - y3) - (y1 - y3) * (x4 - x3);
		double db = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3);
		double ta;
		double tb;
		
		if (Math.abs(d) == 0){
			System.out.println("ERROR: lines not intersected, check with isIntersect() method");
		}
		else{
			ta = da/d;
			tb = db/d;
			if((0 <= ta) && (ta <=1) && (0 <= tb) && (tb <= 1)){
				 xy[0] = (int) (x3 + tb * (x4 - x3));
				 xy[1] = (int) (y1 + ta * (y2 - y1));			
				return xy;
				}
				else
					System.out.println("ERROR: lines not intersected, check with isIntersect() method");
			}
		return xy;		
	}
	/**
	 * This method realizes Delon's triangulation
	 * @param p
	 * @return array of triangles
	 */
	public static ArrayList<Triangle> triangulate(PhiObject p){
		ArrayList<Triangle> tl = new ArrayList<Triangle>();
		
		boolean map[] = new boolean[p.size()];	//used points map
		for (int i = 0; i < map.length; i++){
			map[i] = true;
		}
		
		int count = 0;
		int i1 = 0; int i2 = 1; int i3 = 2;
		int cc = 0;
		while(count != 2){	//sometimes its 2
			
			double x1 = p.getX()[i1];
			double y1 = p.getY()[i1];
			double x2 = p.getX()[i2];
			double y2 = p.getY()[i2];
			double x3 = p.getX()[i3];
			double y3 = p.getY()[i3];
			
			boolean leftside = false;
			boolean hasVertex = false;
			
			if (positiveVectMult(x1, y1, x2, y2, x3, y3)) leftside = true;
			
			for(int u = 0; u < p.size(); u++){
				if (u != i1 && u != i2 && u != i3){
					if (inTriangle(p.getX()[u], p.getY()[u], x1, y1, x2, y2, x3, y3)) hasVertex = true;
				}
			}
			
			if (leftside && !hasVertex){
				
				double xt[] = {x1, x2, x3};
				double yt[] = {y1, y2, y3};
				
				Triangle t = new Triangle(xt, yt, p.getPx(), p.getPy(), p.getPhi());
				tl.add(t);
								
				map[i2] = false;
				
				boolean j = true;
				boolean o = true;
				while(j){
					if (o) i2++;
					if(i2 / p.size() >= 1){//out of range
						i2 = i2 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i2]) j = false;
						else o = true;
					}
				}
				
				j = true;
				o = true;
				while(j){
					if (o) i3++;
					if(i3 / p.size() >= 1){//out of range
						i3 = i3 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i3]) j = false;
						else o = true;
					}
				}
				
			}
			
			else{
				
				boolean j = true;
				boolean o = true;
				while(j){
					if (o) i1++;
					if(i1 / p.size() >= 1){//out of range
						i1 = i1 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i1]) j = false;
						else o = true;
					}
				}
				
				j = true;
				o = true;
				while(j){
					if (o) i2++;
					if(i2 / p.size() >= 1){//out of range
						i2 = i2 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i2]) j = false;
						else o = true;
					}
				}
				
				j = true;
				o = true;
				while(j){
					if (o) i3++;
					if(i3 / p.size() >= 1){//out of range
						i3 = i3 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i3]) j = false;
						else o = true;
					}
				}
				
			}
			
			//define end
			count = 0;
			for (int i = 0; i < map.length; i++)
				if (map[i]) count++;

			
			cc++;
			if (cc > 30000) {
				System.out.println("ERROR: triangulation is out of range!!");
				break;
			}
		}
		return tl;
	}
	
	//cheat method :D
	public static boolean selfIntersect(PhiObject p){
		ArrayList<Triangle> tl = new ArrayList<Triangle>();
		
		boolean map[] = new boolean[p.size()];	//used points map
		for (int i = 0; i < map.length; i++){
			map[i] = true;
		}
		
		int count = 0;
		int i1 = 0; int i2 = 1; int i3 = 2;
		int cc = 0;
		while(count != 2){	//sometimes its 2
			
			double x1 = p.getX()[i1];
			double y1 = p.getY()[i1];
			double x2 = p.getX()[i2];
			double y2 = p.getY()[i2];
			double x3 = p.getX()[i3];
			double y3 = p.getY()[i3];
			
			boolean leftside = false;
			boolean hasVertex = false;
			
			if (positiveVectMult(x1, y1, x2, y2, x3, y3)) leftside = true;
			
			for(int u = 0; u < p.size(); u++){
				if (u != i1 && u != i2 && u != i3){
					if (inTriangle(p.getX()[u], p.getY()[u], x1, y1, x2, y2, x3, y3)) hasVertex = true;
				}
			}
			
			if (leftside && !hasVertex){
				
				double xt[] = {x1, x2, x3};
				double yt[] = {y1, y2, y3};
				
				Triangle t = new Triangle(xt, yt, p.getPx(), p.getPy(), p.getPhi());
				tl.add(t);
								
				map[i2] = false;
				
				boolean j = true;
				boolean o = true;
				while(j){
					if (o) i2++;
					if(i2 / p.size() >= 1){//out of range
						i2 = i2 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i2]) j = false;
						else o = true;
					}
				}
				
				j = true;
				o = true;
				while(j){
					if (o) i3++;
					if(i3 / p.size() >= 1){//out of range
						i3 = i3 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i3]) j = false;
						else o = true;
					}
				}
				
			}
			
			else{
				
				boolean j = true;
				boolean o = true;
				while(j){
					if (o) i1++;
					if(i1 / p.size() >= 1){//out of range
						i1 = i1 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i1]) j = false;
						else o = true;
					}
				}
				
				j = true;
				o = true;
				while(j){
					if (o) i2++;
					if(i2 / p.size() >= 1){//out of range
						i2 = i2 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i2]) j = false;
						else o = true;
					}
				}
				
				j = true;
				o = true;
				while(j){
					if (o) i3++;
					if(i3 / p.size() >= 1){//out of range
						i3 = i3 % p.size();
						o = false;
					}
					else{ //in range
						if (map[i3]) j = false;
						else o = true;
					}
				}
				
			}
			
			//define end
			count = 0;
			for (int i = 0; i < map.length; i++)
				if (map[i]) count++;

			
			cc++;
			if (cc > 30000) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Check point containing in triangle
	 * @param x
	 * @param y
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @param x3
	 * @param y3
	 * @return true if point inside triangle
	 */
	private static boolean inTriangle(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3){
		double a = vectorMultiply(x,y,x1,y1,x2,y2);
		double b = vectorMultiply(x,y,x2,y2,x3,y3);
		double c = vectorMultiply(x,y,x3,y3,x1,y1);
		// Если все три тройки векторов однонаправленные, то  точка(x,y) внутри треугольника
		if (((a > 0) && (b > 0) && (c > 0)) || ((a<0) && (b<0) && (c<0))) return true;
		return false;
	}
	/**
	 * calculates vector multiplication
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @param x3
	 * @param y3
	 * @return vector multiplication
	 */
	public static double vectorMultiply(double x1, double y1, double x2, double y2, double x3, double y3){
		return ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1));
	}
	
	/**
	 * Culclates vector multiply
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @param x3
	 * @param y3
	 * @return true if its more then zero
	 */
	public static boolean positiveVectMult(double x1, double y1, double x2, double y2, double x3, double y3){
		if ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1) > 0) return true;
		else return false;
	}
	
	/**
	 * rotate point above point
	 * @param x
	 * @param y
	 * @param angle
	 * @return array of coordinates
	 */
	public static double[] rotate(double x, double y, double angle){
		double a = angle * Math.PI/180.0;
        double cas_a = Math.cos(a);
        double sin_a = Math.sin(a);
        double c[] = new double[2];
        c[0] = x*cas_a - y*sin_a;
        c[1] = x*sin_a + y*cas_a;
        	
        return c;
	}
	
	public static boolean intersect(PhiObject o1, PhiObject o2){
		for(int i = 0; i < o1.size(); i++){
			if (o2.poly().contains(o1.getIntX()[i], o1.getIntY()[i])) return true;
		}
		for(int i = 0; i < o2.size(); i++){
			if (o1.poly().contains(o2.getIntX()[i], o2.getIntY()[i])) return true;
		}
		return false;
	}
	
	/**
	 * Smart object intersection, uses improved algorithm
	 * @param o1
	 * @param o2
	 * @return true if object are intersected
	 */
	public static boolean smart_intersect(PhiObject o1, PhiObject o2){
		for(int i = 0; i < o1.size(); i++){
			if(i != o1.size()-1){
				for(int j = 0; j < o2.size(); j++){
					if(j != o2.size()-1){
						if (isIntersect(o1.getX()[i], o1.getY()[i], o1.getX()[i+1], o1.getY()[i+1],    o2.getX()[j], o2.getY()[j], o2.getX()[j+1], o2.getY()[j+1])) 
							return true;
					}
					else{
						if (isIntersect(o1.getX()[i], o1.getY()[i], o1.getX()[i+1], o1.getY()[i+1],    o2.getX()[j], o2.getY()[j], o2.getX()[0], o2.getY()[0]))
							return true;
					}
				}
			}
			else{
				for(int j = 0; j < o2.size(); j++){
					if(j != o2.size()-1){
						if (isIntersect(o1.getX()[i], o1.getY()[i], o1.getX()[0], o1.getY()[0],    o2.getX()[j], o2.getY()[j], o2.getX()[j+1], o2.getY()[j+1])) 
							return true;
					}
					else{
						if (isIntersect(o1.getX()[i], o1.getY()[i], o1.getX()[0], o1.getY()[0],    o2.getX()[j], o2.getY()[j], o2.getX()[0], o2.getY()[0]))
							return true;
					}
				}
			}
		}
		
		if(intersect(o1, o2)) return true;
		
		return false;
	}
	/**
	 * Русиш тестиш
	 */
	public void union(){
		//TODO
	}
	
	public void createHull(){
		//TODO
	}
	
	public void minToLineLength(){
		//TODO
	}
	
	public void minToLinePoint(){
		//TODO
	}
	
}
